package mk.jug.academy.service;

import java.util.List;

import mk.jug.academy.exception.BusinessException;
import mk.jug.academy.model.Lecturer;
import mk.jug.academy.model.status.LecturerStatus;
import mk.jug.academy.preconditions.ErrorCodes;
import mk.jug.academy.preconditions.PreconditionExceptions;
import mk.jug.academy.testing.bdd.IntegrationBehavior;
import mk.jug.academy.testing.factory.LecturerTestFactory;

import org.springframework.beans.factory.annotation.Autowired;

import com.googlecode.instinct.marker.annotate.Specification;
import com.googlecode.instinct.marker.annotate.Specification.SpecificationState;
import com.googlecode.instinct.marker.annotate.Subject;

/**
 * An integration test unit examining the behavior revolving around the
 * {@link Lecturer} registration and assignment/de-assignment process.
 *
 * @author Ice.Penov
 */
public class LecturerLifecycle extends IntegrationBehavior {
	
	@Autowired private LecturerService lecturerService;
	@Subject private Lecturer lecturer;
	
	@Override
	protected void beforeHook() {
		lecturer = LecturerTestFactory.createLecturer();
	}
	
	/**
	 * User action:
	 * A {@link Lecturer} tries to register without supplying the mandatory info.
	 * 
	 * Expected behavior:
	 * 1. The registration process should fail with {@link IllegalArgumentException}
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void failToRegisterLecturerWhenMandatoryInformationIsMissing() {
		//missing first name.
		new ExceptionExpectation() {
			public void execute() {
				lecturer.setFirstName(null);
				lecturerService.registerLecturer(lecturer);
			}
		}.checkFor(IllegalArgumentException.class, PreconditionExceptions.MISSING_FIRST_NAME);
		lecturer = LecturerTestFactory.createLecturer();
		//missing last name.
		new ExceptionExpectation() {
			public void execute() {
				lecturer.setLastName(null);
				lecturerService.registerLecturer(lecturer);
			}
		}.checkFor(IllegalArgumentException.class, PreconditionExceptions.MISSING_LAST_NAME);
		lecturer = LecturerTestFactory.createLecturer();
		//missing email.
		new ExceptionExpectation() {
			public void execute() {
				lecturer.setEmail(null);
				lecturerService.registerLecturer(lecturer);
			}
		}.checkFor(IllegalArgumentException.class, PreconditionExceptions.MISSING_EMAIL);
	}
	
	/**
	 * User action:
	 * A {@link Lecturer} with already registered email tries to register.
	 * 
	 * Expected behavior:
	 * 1. Throw a {@link BusinessException} with the appropriate error code.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void failToRegisterLecturerWithExistingEmail() {
		lecturerService.registerLecturer(lecturer);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.registerLecturer(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_EMAIL_EXISTS);
	}
	
	/**
	 * User action:
	 * A {@link Lecturer} with complete information tries to register.
	 * 
	 * Expected behavior:
	 * 1. The {@link Lecturer} should be persisted in the database.
	 * 2. The {@link Lecturer} should be in the {@link LecturerStatus#SIGNED_UP}
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void proceedWithLecturerRegistrationWhenInformationIsComplete() {
		lecturerService.registerLecturer(lecturer);
		List<Lecturer> lecturers = lecturerService.listAll(1, 2).getItems();
		expect.that(lecturers).containsItem(lecturer);
		expect.that(lecturers).isOfSize(1);
		//also in the singed up lecturers pool:
		List<Lecturer> signedUpLecturers = lecturerService.listSignedUp(1, 2).getItems();
		expect.that(signedUpLecturers).containsItem(lecturer);
		expect.that(signedUpLecturers).isOfSize(1);
	}
	
	/**
	 * User action:
	 * The admin tries to active {@link Lecturer} with state other than {@link LecturerStatus#SIGNED_UP}
	 * 
	 * Expected behavior:
	 * 1. Throw an {@link IllegalStateException} with an appropriate error code.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void failToAcceptLecturerRegistrationIfNotInSignedUpState() {
		lecturerService.registerLecturer(lecturer);		
		lecturer.setStatus(LecturerStatus.ACTIVE);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.acceptRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);
		lecturer.setStatus(LecturerStatus.REJECTED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.acceptRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);
		lecturer.setStatus(LecturerStatus.RETIRED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.acceptRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);		
	}
	
	/**
	 * User action:
	 * The signed up {@link Lecturer} is being accepted by the admins.
	 * 
	 * Expected behavior:
	 * 1. The {@link Lecturer} should be available in the active lecturers pool
	 * 2. The {@link Lecturer} should no longer be available in the signed up lecturers pool
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void acceptLecturerRegistration() {
		lecturerService.registerLecturer(lecturer);		
		lecturerService.acceptRegistration(lecturer);
		expect.that(lecturerService.listActive(1, 2).getItems()).containsItem(lecturer);
		expect.that(lecturerService.listSignedUp(1, 2).getItems()).doesNotContainItem(lecturer);
	}
	
	/**
	 * User action:
	 * The admin tries to reject {@link Lecturer} with state other than {@link LecturerStatus#SIGNED_UP}
	 * 
	 * Expected behavior:
	 * 1. Throw an {@link IllegalStateException} with an appropriate error code.
	 */	
	@Specification(state = SpecificationState.COMPLETE)
	public void failToRejectLecturerIfNotInSignedUpState() {
		lecturerService.registerLecturer(lecturer);		
		lecturer.setStatus(LecturerStatus.ACTIVE);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.rejectRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);
		lecturer.setStatus(LecturerStatus.REJECTED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.rejectRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);
		lecturer.setStatus(LecturerStatus.RETIRED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.rejectRegistration(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_SIGNED_UP_STATE);				
	}
	
	/**
	 * User action:
	 * The {@link Lecturer} is being rejected by the admins.
	 * 
	 * Expected behavior:
	 * 1. The {@link Lecturer} should be available in the rejected lecturers pool
	 * 2. The {@link Lecturer} should no longer be available in the signed up lecturers pool
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void rejectLecturerRegistration() {
		lecturerService.registerLecturer(lecturer);		
		lecturerService.rejectRegistration(lecturer);
		expect.that(lecturerService.listRejected(1, 2).getItems()).containsItem(lecturer);
		expect.that(lecturerService.listSignedUp(1, 2).getItems()).doesNotContainItem(lecturer);		
	}	
	
	/**
	 * User action:
	 * The {@link Lecturer} with state other than {@link LecturerStatus#ACTIVE} is being retired by the admins.
	 * 
	 * Expected behavior:
	 * 1. Throw an {@link IllegalStateException} with an appropriate message.
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void failToRetireLecturerIfNotInActiveState() {
		lecturerService.registerLecturer(lecturer);		
		lecturer.setStatus(LecturerStatus.SIGNED_UP);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.retireLecturer(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_ACTIVE_STATE);
		lecturer.setStatus(LecturerStatus.REJECTED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.retireLecturer(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_ACTIVE_STATE);
		lecturer.setStatus(LecturerStatus.RETIRED);
		new ExceptionExpectation() {
			public void execute() {
				lecturerService.retireLecturer(lecturer);
			}
		}.checkForErrorCode(BusinessException.class, ErrorCodes.ERROR_LECTURER_ACTIVE_STATE);						
	}
	
	/**
	 * User action:
	 * The {@link Lecturer} is being retired by the admins.
	 * 
	 * Expected behavior:
	 * 1. The {@link Lecturer} should be available in the retired lecturers pool
	 * 2. The {@link Lecturer} should no longer be available in the active lecturers pool
	 */
	@Specification(state = SpecificationState.COMPLETE)
	public void retireLecturer() {
		lecturerService.registerLecturer(lecturer);
		lecturerService.acceptRegistration(lecturer);
		lecturerService.retireLecturer(lecturer);
		expect.that(lecturerService.listRetired(1, 2).getItems()).containsItem(lecturer);
		expect.that(lecturerService.listSignedUp(1, 2).getItems()).doesNotContainItem(lecturer);				
	}
		
}